package com.omuao.tool.lang.tree;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ReflectUtil;
import com.omuao.tool.lang.tree.anotation.TreeChildren;
import com.omuao.tool.lang.tree.anotation.TreeId;
import com.omuao.tool.lang.tree.anotation.TreePid;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 树工具类
 */
public class TreeUtils {


	/**
	 * 默认ID列
	 */
	private static final String DEFAULT_ID_COLUMN = "id";

	/**
	 * 默认父ID列
	 */
	private static final String DEFAULT_PID_COLUMN = "pid";

	/**
	 * 默认孩子列
	 */
	private static final String DEFAULT_CHILDREN_COLUMN = "children";

	/**
	 * 转换目标数据集，并输出带有Tree注解的类
	 *
	 * @param dataList 原始数据
	 * @param rClass   输出类型
	 * @param <T>      原始类型
	 * @param <R>      输出类型
	 * @return 输出带有Tree的数据
	 */
	public static <T, R> List<R> findAnnotationTreeRoots(List<T> dataList, Class<R> rClass) {
		return findAnnotationRoots(annotationValueOf(dataList, rClass), rClass);
	}

	/**
	 * 转换目标数据集，并输出带有Tree注解的类
	 *
	 * @param dataList 原始数据
	 * @param rClass   输出类型
	 * @param rootVal  根节点的值
	 * @param <T>      原始类型
	 * @param <R>      输出类型
	 * @return 输出带有Tree的数据
	 */
	public static <T, R> List<R> findAnnotationTreeRoots(List<T> dataList, Class<R> rClass, Object rootVal) {
		return findAnnotationRoots(annotationValueOf(dataList, rClass), rClass, rootVal);
	}

	/**
	 * 转换目标数据集，并输出带有Tree注解的类
	 *
	 * @param dataList Tree节点数据
	 * @param rClass   输出类型
	 * @param rootVal  根节点的值
	 * @param <T>      原始类型
	 * @param <R>      输出类型
	 * @return 输出带有Tree的数据
	 */
	public static <T, R> List<R> findAnnotationRoots(List<TreeNode<T>> dataList, Class<R> rClass, Object rootVal) {
		Field children = TreeAnnotationUtils.findFieldByAnnotation(rClass, TreeChildren.class);
		Field pid = TreeAnnotationUtils.findFieldByAnnotation(rClass, TreePid.class);
		List<R> list = TreeUtils.convert2Bean(dataList, rClass, children.getName());
		return findBeanRoots(list, pid.getName(), rootVal);
	}

	/**
	 * 转换目标数据集，并输出带有Tree注解的类
	 *
	 * @param dataList Tree节点数据
	 * @param rClass   输出类型
	 * @param <T>      原始类型
	 * @param <R>      输出类型
	 * @return 输出带有Tree的数据
	 */
	public static <T, R> List<R> findAnnotationRoots(List<TreeNode<T>> dataList, Class<R> rClass) {
		Field children = TreeAnnotationUtils.findFieldByAnnotation(rClass, TreeChildren.class);
		Field pid = TreeAnnotationUtils.findFieldByAnnotation(rClass, TreePid.class);
		List<R> list = TreeUtils.convert2Bean(dataList, rClass, children.getName());
		Object rootTag = TreeAnnotationUtils.findTreeRootHandlerRootValue(rClass);
		return findBeanRoots(list, pid.getName(), rootTag);
	}

	/**
	 * 转换目标数据集，并输出树节点List
	 *
	 * @param dataList 原始数据
	 * @param rClass   输出类型
	 * @param <T>      原始类型
	 * @param <R>      输出类型
	 * @return 输出树节点List
	 */
	public static <T, R> List<TreeNode<T>> annotationValueOf(List<T> dataList, Class<R> rClass) {
		Field pid = TreeAnnotationUtils.findFieldByAnnotation(rClass, TreePid.class);
		Field id = TreeAnnotationUtils.findFieldByAnnotation(rClass, TreeId.class);
		Map<String, String> mappings = new HashMap<>();
		mappings.put(DEFAULT_ID_COLUMN, id.getName());
		mappings.put(DEFAULT_PID_COLUMN, pid.getName());
		return TreeUtils.valueOf(dataList, mappings);
	}

	/**
	 * 转换目标数据集，并输出树节点List
	 *
	 * @param dataList 原始数据
	 * @param <T>      原始类型
	 * @return 输出树节点List
	 */
	public static <T> List<TreeNode<T>> valueOf(List<T> dataList) {
		Map<String, String> mappings = new HashMap<>();
		mappings.put(DEFAULT_ID_COLUMN, DEFAULT_ID_COLUMN);
		mappings.put(DEFAULT_PID_COLUMN, DEFAULT_PID_COLUMN);
		return TreeUtils.valueOf(dataList, mappings);
	}

	/**
	 * 转换目标数据集，并输出树节点List
	 *
	 * @param dataList     原始数据
	 * @param treeMappings 节点映射关系
	 * @param <T>          原始类型
	 * @return 输出树节点List
	 */
	public static <T> List<TreeNode<T>> valueOf(List<T> dataList, Map<String, String> treeMappings) {
		List<TreeNode<T>> treeNodes = dataList.stream().map(data -> {
			Object id = ReflectUtil.getFieldValue(data, treeMappings.get(DEFAULT_ID_COLUMN));
			Object pid = ReflectUtil.getFieldValue(data, treeMappings.get(DEFAULT_PID_COLUMN));
			return new TreeNode<T>(id, pid, data);
		}).collect(Collectors.toList());
		treeNodes.forEach(e -> e.fillData(treeNodes));
		return treeNodes;
	}

	/**
	 * 查找根节点
	 *
	 * @param dataList 节点数据
	 * @param <T>      任意类型
	 * @return 根节点List
	 */
	public static <T> List<TreeNode<T>> findRoots(List<TreeNode<T>> dataList) {
		return findRoots(dataList, null);
	}

	/**
	 * 查找根节点
	 *
	 * @param dataList 节点数据
	 * @param rootVal  根节点值
	 * @param <T>      任意类型
	 * @return 根节点List
	 */
	public static <T> List<TreeNode<T>> findRoots(List<TreeNode<T>> dataList, Object rootVal) {
		if (CollectionUtil.isEmpty(dataList)) {
			return null;
		}
		if (rootVal == null) {
			return dataList.stream().filter(TreeNode::isRootNode).collect(Collectors.toList());
		}
		return dataList.stream().filter(e -> e.isRootNode() || e.isRootNode(rootVal)).collect(Collectors.toList());
	}

	/**
	 * 转换为ListMap
	 *
	 * @param dataList Tree节点数据
	 * @param <T>      任意类型
	 * @return ListMap对象
	 */
	public static <T> List<Map<String, Object>> convert2Map(List<TreeNode<T>> dataList) {
		return dataList.stream().map(TreeNode::convertToMap).collect(Collectors.toList());
	}

	/**
	 * 转换为Bean
	 *
	 * @param dataList Tree节点数据
	 * @param tClass   Bean类
	 * @param <T>      任意类型
	 * @param <R>      任意类型
	 * @return Bean对象
	 */
	public static <T, R> List<R> convert2Bean(List<TreeNode<T>> dataList, Class<R> tClass) {
		return convert2Bean(dataList, tClass, null);
	}

	/**
	 * 转换为Bean
	 *
	 * @param dataList       Tree节点数据
	 * @param rClass         输出的Bean类
	 * @param childrenColumn 孩子节点字段名称
	 * @param <T>            任意类型
	 * @param <R>            任意类型
	 * @return Bean对象
	 */
	public static <T, R> List<R> convert2Bean(List<TreeNode<T>> dataList, Class<R> rClass, String childrenColumn) {
		if (childrenColumn == null) {
			return dataList.stream().map(e -> e.convertToBean(rClass)).collect(Collectors.toList());
		}
		return dataList.stream().map(e -> e.convertToBean(rClass, childrenColumn)).collect(Collectors.toList());
	}

	/**
	 * 查找Root节点List
	 *
	 * @param dataList 数据
	 * @param <T>      任意类型
	 * @return Root节点List
	 */
	public static <T> List<T> findBeanRoots(List<T> dataList) {
		return findBeanRoots(dataList, DEFAULT_PID_COLUMN, null);
	}

	/**
	 * 查找Root节点List
	 *
	 * @param dataList   数据
	 * @param rootColumn 父级节点名称
	 * @param <T>        任意类型
	 * @return Root节点List
	 */
	public static <T> List<T> findBeanRoots(List<T> dataList, String rootColumn) {
		return findBeanRoots(dataList, rootColumn, null);
	}

	/**
	 * 查找Root节点List
	 *
	 * @param dataList   数据
	 * @param rootColumn 父级节点名称
	 * @param rootVal    孩子节点字段名称
	 * @param <T>        任意类型
	 * @return Root节点List
	 */
	public static <T> List<T> findBeanRoots(List<T> dataList, String rootColumn, Object rootVal) {
		if (CollectionUtil.isEmpty(dataList)) {
			return null;
		}
		if (rootVal == null) {
			return dataList.stream().filter(e -> ReflectUtil.getFieldValue(e, rootColumn) == null).collect(Collectors.toList());
		}
		return dataList.stream().filter(e -> ReflectUtil.getFieldValue(e, rootColumn) == null || ReflectUtil.getFieldValue(e, rootColumn).equals(rootVal)).collect(Collectors.toList());
	}

	/**
	 * 树注解工具类
	 */
	private static class TreeAnnotationUtils {
		private static <R> Field findFieldByAnnotation(Class<R> targetClass, Class<? extends Annotation> annotationType) {
			Field field = Arrays.stream(ReflectUtil.getFields(targetClass)).filter(e -> AnnotationUtil.hasAnnotation(e, annotationType)).findFirst().get();
			return field;
		}

		private static <R> Object findTreeRootHandlerRootValue(Class<R> targetClass) {
			TreePid root = Arrays.stream(ReflectUtil.getFields(targetClass)).filter(e -> AnnotationUtil.hasAnnotation(e, TreePid.class)).filter(e -> {
				TreePid mapping = AnnotationUtil.getAnnotation(e, TreePid.class);
				return mapping.rootTag() != null && !mapping.rootTag().equals(TreeRootHandler.class);
			}).map(e -> {
				TreePid mapping = AnnotationUtil.getAnnotation(e, TreePid.class);
				return mapping;
			}).findFirst().orElse(null);
			Object rootTag = TreeRootHandler.defaultHandler().rootTag();
			if (root != null) {
				Class<? extends TreeRootHandler> rootHandler = root.rootTag();
				TreeRootHandler handler = ReflectUtil.newInstance(rootHandler);
				rootTag = handler.rootTag();
			} else {
				if (TreeRootHandler.class.isAssignableFrom(targetClass)) {
					TreeRootHandler handler = (TreeRootHandler) ReflectUtil.newInstance(targetClass);
					rootTag = handler.rootTag();
				}
			}
			return rootTag;
		}
	}

	/**
	 * 树节点
	 *
	 * @param <T> 任意类型
	 */
	public static class TreeNode<T> {

		/**
		 * ID
		 */
		private Object id;

		/**
		 * 父级ID
		 */
		private Object pid;

		/**
		 * 数据
		 */
		private T data;

		/**
		 * 子级数据
		 */
		private List<TreeNode<T>> children;

		public TreeNode() {
		}

		public TreeNode(Object id, Object pid, T data) {
			this.id = id;
			this.pid = pid;
			this.data = data;
		}

		/**
		 * 填充数据
		 *
		 * @param nodes 数据
		 */
		public void fillData(List<TreeNode<T>> nodes) {
			if (CollectionUtil.isEmpty(nodes)) {
				return;
			}
			if (id != null) {
				this.children = nodes.stream().filter(e -> id.equals(e.getPid()) && !id.equals(e.getId())).collect(Collectors.toList());
			}
		}

		/**
		 * 根节点
		 *
		 * @return true 根 false 非根
		 */
		public boolean isRootNode() {
			if (pid == null) {
				return true;
			}
			return false;
		}

		/**
		 * 根节点
		 *
		 * @param rootVal 根节点的值
		 * @return true 根 false 非根
		 */
		public boolean isRootNode(Object rootVal) {
			if (isRootNode() || pid.equals(rootVal)) {
				return true;
			}
			return false;
		}

		/**
		 * 转换为Map
		 *
		 * @return Map
		 */
		public Map<String, Object> convertToMap() {
			Map<String, Object> map = BeanUtil.beanToMap(this.getData());
			if (CollectionUtil.isNotEmpty(children)) {
				map.put(DEFAULT_CHILDREN_COLUMN, children.stream().map(e -> e.convertToMap()).collect(Collectors.toList()));
			}
			return map;
		}

		/**
		 * 转换为Bean
		 *
		 * @param targetClass 目标类型
		 * @param <R>         任意类型
		 * @return 目标类型对象
		 */
		public <R> R convertToBean(Class<R> targetClass) {
			return convertToBean(targetClass, null);
		}

		/**
		 * 转换为Bean
		 *
		 * @param targetClass       目标类型
		 * @param childrenColumnTag 目标类型孩子节点
		 * @param <R>               任意类型
		 * @return 目标类型对象
		 */
		public <R> R convertToBean(Class<R> targetClass, String childrenColumnTag) {
			if (childrenColumnTag == null) {
				childrenColumnTag = DEFAULT_CHILDREN_COLUMN;
			}
			return convertToBean(targetClass, null, childrenColumnTag);
		}

		/**
		 * 转换为Bean
		 *
		 * @param targetClass       目标类型
		 * @param mapping           目标类型与data类型的映射关系
		 * @param childrenColumnTag 目标类型孩子节点
		 * @param <R>               任意类型
		 * @return 目标类型对象
		 */
		public <R> R convertToBean(Class<R> targetClass, Map<String, String> mapping, String childrenColumnTag) {
			Object data = this.getData();
			if (data == null) {
				data = new Object();
			}
			Map<String, Object> map = BeanUtil.beanToMap(data);
			if (CollectionUtil.isNotEmpty(mapping)) {
				Map<String, Object> finalTarget = new HashMap<>();
				mapping.keySet().forEach(key -> finalTarget.put(key, map.get(key)));
				if (CollectionUtil.isNotEmpty(children)) {
					finalTarget.put(childrenColumnTag, children.stream().map(e -> e.convertToBean(targetClass, childrenColumnTag)).collect(Collectors.toList()));
				}
				return BeanUtil.mapToBean(finalTarget, targetClass, true, CopyOptions.create());
			}

			if (CollectionUtil.isNotEmpty(children)) {
				map.put(childrenColumnTag, children.stream().map(e -> e.convertToBean(targetClass, childrenColumnTag)).collect(Collectors.toList()));
			}
			return BeanUtil.mapToBean(map, targetClass, true, CopyOptions.create());
		}

		public Object getId() {
			return id;
		}

		public void setId(Object id) {
			this.id = id;
		}

		public Object getPid() {
			return pid;
		}

		public void setPid(Object pid) {
			this.pid = pid;
		}

		public T getData() {
			return data;
		}

		public void setData(T data) {
			this.data = data;
		}

		public List<TreeNode<T>> getChildren() {
			return children;
		}

		public void setChildren(List<TreeNode<T>> children) {
			this.children = children;
		}
	}

}
